/* eslint-disable */
import _ from 'underscore';

const CCNODE_PROPS = [
    ['name', 'New Node'],
    ['width', 0],
    ['height', 0],
    ['active', true],
    ['opacity', 255],
    ['cascadeOpacity', true],
    // ['color', cc.color(255, 255, 255)],//fromHEX
    ['x', 0],
    ['y', 0],
    ['rotation', 0],
    ['angle', 0],
    ['scale', 1],
    ['scaleX', 1],
    ['scaleY', 1],
    ['anchorX', 0.5],
    ['anchorY', 0.5],
    ['skewX', 0],
    ['skewY', 0],
    ['zIndex', 0],
];

const CCNODE_EVENTS = [
    ['onTouchStart', cc.Node.EventType.TOUCH_START],
    ['onTouchMove', cc.Node.EventType.TOUCH_MOVE],
    ['onTouchEnd', cc.Node.EventType.TOUCH_END],
    ['onTouchCancel', cc.Node.EventType.TOUCH_CANCEL],
    ['onMouseDown', cc.Node.EventType.MOUSE_DOWN],
    ['onMouseMove', cc.Node.EventType.MOUSE_MOVE],
    ['onMouseEnter', cc.Node.EventType.MOUSE_ENTER],
    ['onMouseLeave', cc.Node.EventType.MOUSE_LEAVE],
    ['onMouseUp', cc.Node.EventType.MOUSE_UP],
    ['onMouseWheel', cc.Node.EventType.MOUSE_WHEEL],
    ['onPositionChanged', 'position-changed'],
    ['onRotationChanged', 'rotation-changed'],
    ['onScaleChanged', 'scale-changed'],
    ['onSizeChanged', 'size-changed'],
    ['onAnchorChanged', 'anchor-changed'],
    ['onChildReorder', 'child-reorder'],
    ['onClick', 'click'],
    ['onFinished', 'finished'],
];

function initComponentProps(instance, props = {}) {
    if (instance instanceof cc.Sprite) {
        initSpriteComponentProps(instance, props);
    } else {
        applyComponentProps(instance, props);
    }
}

function updateComponentProps(instance, oldProps, newProps = {}) {
    applyComponentProps(instance, newProps);
}

function applyComponentProps(instance, props = {}) {
    Object.keys(props).forEach(key => {
        instance[key] = props[key];
    });
}

function hexToColor(hexString){
    hexString = (hexString.indexOf('#') === 0) ? hexString.substring(1) : hexString;
    if(hexString.length===3){
        let r = parseInt(hexString.substr(0, 1), 16) || 0;
        let g = parseInt(hexString.substr(1, 1), 16) || 0;
        let b = parseInt(hexString.substr(2, 1), 16) || 0;
        return cc.color(r<<4|r,g<<4|g,b<<4|b);
    }
    let r = parseInt(hexString.substr(0, 2), 16) || 0;
    let g = parseInt(hexString.substr(2, 2), 16) || 0;
    let b = parseInt(hexString.substr(4, 2), 16) || 0;
    let a = parseInt(hexString.substr(6, 2), 16) || 255;
    return cc.color(r,g,b,a);
}

function convColor(color){
    if(typeof color === "string"){
        return hexToColor(color);//cc.color().fromHEX
    }
    return color;
}

function applyNodeProps(node, props = {}) {
    CCNODE_PROPS.forEach(([key]) => {
        if (key in props) {
            node[key] = props[key];
        }
    });

    if(props["color"]){
        node["color"] = convColor(props["color"]);
    }

    CCNODE_EVENTS.forEach(([name, type]) => {
        if (props[name]) {
            node.on(type, props[name]);
        }
    });
}

function updateNodeProps(node, oldProps = {}, newProps = {}) {
    CCNODE_PROPS.forEach(([key, defaultValue]) => {
        if (oldProps[key] !== newProps[key]) {
            if (key in newProps) {
                node[key] = newProps[key];
            } else {
                node[key] = defaultValue;
            }
        }
    });

    if (oldProps["color"] !== newProps["color"]) {
        if (newProps["color"]) {
            node["color"] = convColor(newProps["color"]);
        } else {
            node["color"] = cc.color(255, 255, 255);
        }
    }

    CCNODE_EVENTS.forEach(([name, type]) => {
        const newProp = newProps[name];
        const oldProp = oldProps[name];

        if (newProp === oldProp) {
            return;
        }

        if (oldProp) {
            node.off(type, oldProp);
        }

        if (newProp) {
            node.on(type, newProp);
        }
    });
}

function initSpriteComponentProps(instance, props) {
    const needPresetProps = [
        'spriteFrame',
        'type',
        'sizeMode',
        'fillType',
        'fillCenter',
        'fillStart',
        'fillRange',
        'trim',
    ];
    Object.keys(props).forEach(key => {
        if (needPresetProps.indexOf(key) === -1) {
            instance[key] = props[key];
        } else {
            instance[`_${key}`] = props[key];
        }
    });
}

function create(props = {},components = []) {
    const node = new cc.Node();
    applyNodeProps(node, props);

    components = components.filter((c)=>(c&&c.type));

    for (const { type, props } of components) {
        const instance = node.addComponent(type);
        initComponentProps(instance, props);
    }

    return node;
}

function updateNodeComps(node, oldComps= [], newComps= []) {
    oldComps = oldComps.filter((c)=>(c&&c.type));
    newComps = newComps.filter((c)=>(c&&c.type));
    const newCompTypes = newComps.map(comp => comp.type);
    const oldCompTypes = oldComps.map(comp => comp.type);
    const needUpdateComponents = _.intersection(newCompTypes, oldCompTypes);
    const needRemoveComponents = _.difference(oldCompTypes, newCompTypes);
    const needAppendComponents = _.difference(newCompTypes, oldCompTypes);

    const getPropsByComponentType = (list, type) => {
        for (let comp of list) {
            if (comp.type === type) {
                return comp.props;
            }
        }
        return null;
    };

    // console.log("updateNodeComps needUpdateComponents",needUpdateComponents);

    for (const type of needUpdateComponents) {
        const oldProps = getPropsByComponentType(oldComps, type);
        const newProps = getPropsByComponentType(newComps, type);

        updateComponentProps(node.getComponent(type), oldProps, newProps);
    }

    for (const type of needRemoveComponents) {
        node.removeComponent(type);
    }

    for (const type of needAppendComponents) {
        const instance = node.addComponent(type);
        const props = getPropsByComponentType(newComps, type);
        initComponentProps(instance, props);
    }
}

//////////////////////////////////////////////////////////////
function SizeFromString(str) {
    let arr = str.substr(1, str.length - 2).split(',');
    return cc.size(parseInt(arr[0]), parseInt(arr[1]));
}

function PointFromString(str) {
    let arr = str.substr(1, str.length - 2).split(',');
    return cc.v2(parseInt(arr[0]), parseInt(arr[1]));
}

function RectFromString(str) {
    let arr = str.substr(1, str.length - 2).split('},{');
    let pos = arr[0].substr(1, arr[0].length - 1).split(',');
    let sz = arr[1].substr(0, arr[1].length - 1).split(',');
    return cc.rect(
        parseInt(pos[0]),
        parseInt(pos[1]),
        parseInt(sz[0]),
        parseInt(sz[1])
    );
}

function createAtlasFrames(frames, texture) {
    let spriteFrames = {};
    for (let key in frames) {
        //console.log('load_atlas_frames', key, frames[key]);
        let attrs = frames[key];
        if(attrs.spriteOffset){
            let spriteOffset = PointFromString(attrs.spriteOffset);
            let spriteSourceSize = SizeFromString(attrs.spriteSourceSize);
            let textureRect = RectFromString(attrs.textureRect);
            let textureRotated = attrs.textureRotated;
    
            let frame = new cc.SpriteFrame(
                texture,
                textureRect,
                textureRotated,
                spriteOffset,
                spriteSourceSize
            );
            spriteFrames[key] = frame;
        }else if(attrs.width&&attrs.height){
            let frame = new cc.SpriteFrame(
                texture,
                cc.rect(attrs.x,attrs.y,attrs.width,attrs.height),
                false,
                cc.v2(attrs.offsetX,attrs.offsetY),
                cc.size(attrs.originalWidth,attrs.originalHeight)
            );
            spriteFrames[key] = frame;
        }
    }
    return spriteFrames;
}

function loadAtlasFrames(plist, pfile,cb) {
    var frames = null;
    var texture = null;
    function create(){
        if(frames&&texture){
            cb(createAtlasFrames(frames,texture));
        }
    }
    cc.assetManager.loadRemote(plist, function (err, atlas) {
        // console.log("loadRemote atlas",atlas._nativeAsset);
        frames = atlas._nativeAsset.frames;
        create();
    });
    cc.assetManager.loadRemote(pfile, function (err, tex) {
        // console.log("loadRemote atlas",atlas._nativeAsset);
        texture = tex;
        create();
    });
}

export default {
    create,
    updateNodeProps,
    updateNodeComps,
    convColor,
    hexToColor,
    createAtlasFrames,
    loadAtlasFrames,
};
